﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management;
using System.Net;
using System.Net.NetworkInformation;


namespace Applib.NetWorks
{
    public class NetworkSetup
    {
        #region message map
        static Dictionary<string, string> dnsswitchErrors = new Dictionary<string, string>()
        {
            {"0", "Successful completion, no reboot required"},
            {"1", "Successful completion, reboot required"},
            {"64", "Method not supported on this platform"},
            {"65", "Unknown failure"},
            {"66", "Invalid subnet mask"},
            {"67", "An error occurred while processing an Instance that was returned"},
            {"68", "Invalid input parameter"},
            {"69", "More than 5 gateways specified"},
            {"70", "Invalid IP address"},
            {"71", "Invalid gateway IP address"},
            {"72", "An error occurred while accessing the Registry for the requested information"},
            {"73", "Invalid domain name"},
            {"74", "Invalid host name"},
            {"75", "No primary/secondary WINS server defined"},
            {"76", "Invalid file"},
            {"77", "Invalid system path"},
            {"78", "File copy failed"},
            {"79", "Invalid security parameter"},
            {"80", "Unable to configure TCP/IP service"},
            {"81", "Unable to configure DHCP service"},
            {"82", "Unable to renew DHCP lease"},
            {"83", "Unable to release DHCP lease"},

            {"84", "IP not enabled on adapter"},
            {"85", "IPX not enabled on adapter"},
            {"86", "Frame/network number bounds error"},
            {"87", "Invalid frame type"},
            {"88", "Invalid network number"},
            {"89", "Duplicate network number"},
            {"90", "Parameter out of bounds"},
            {"91", "Access denied"},
            {"92", "Out of memory"},
            {"93", "Already exists"},
            {"94", "Path, file or object not found"},
            {"95", "Unable to notify service"},
            {"96", "Unable to notify DNS service"},
            {"97", "Interface not configurable"},
            {"98", "Not all DHCP leases could be released/renewed"},
            {"100", "DHCP not enabled on adapter"},

        };

        static Dictionary<string, string> setNewDomainErrors = new Dictionary<string, string>()
            {
            {"0", "success"},
            {"5", "Access is denied"},
            {"87", "The parameter is incorrect"},
            {"110", "The system cannot open the specified object"},
            {"1323","Unable to update the password"},
            {"1326", "Logon failure: unknown username or bad password"},
            {"1355","The specified domain either does not exist or could not be contacted"},
            {"2224","The account already exists"},
            {"2691","The machine is already joined to a domain"},
            {"2692","The machine is not currently joined to a domain"}
            };

        #endregion

        public delegate void MessageHanlder(string msg);
        public event MessageHanlder OnDnsChanged;
        public event MessageHanlder OnDomainChanged;

        public string HostName { get; set; }
        public string IPv4 { get; set; }
        public string SubNetMask { get; set; }
        public string DefaultGateWay { get; set; }
        public string PrimaryDns { get; set; }
        public string SecondDns { get; set; }


        public string DomainName { get; set; }
        public string DomainUser { get; set; }
        public string DomainPassword { get; set; }


        public NetworkSetup()
        {

        }

        public void SetIPAddress(bool staticIp)
        {
            using (Wmi wmi = new Wmi("Win32_NetworkAdapterConfiguration"))
            {
                wmi.Foreach(o =>
                    {
                        if (!(bool)o["IPEnabled"])
                            return false;

                        ManagementBaseObject par = null;
                        ManagementBaseObject ret = null;
                        int returnCode = 0;
                        if (!string.IsNullOrEmpty(PrimaryDns))
                        {
                            par = o.GetMethodParameters("SetDNSServerSearchOrder");
                            par["DNSServerSearchOrder"] = new string[] { PrimaryDns, /* SecondDns */ };
                            ret = o.InvokeMethod("SetDNSServerSearchOrder", par, null);

                            returnCode = int.Parse(ret["returnValue"].ToString());
                        }
                        else
                        {
                            object r = o.InvokeMethod("SetDNSServerSearchOrder", null);
                            returnCode = int.Parse(r.ToString());
                        }

                        if (dnsswitchErrors.ContainsKey(returnCode.ToString()))
                        {
                            Debug.WriteLine(dnsswitchErrors[returnCode.ToString()], "SetDNSServerSearchOrder");
                            if (OnDnsChanged != null)
                                OnDnsChanged("SetDNSServerSearchOrder" + dnsswitchErrors[returnCode.ToString()]);
                        }
                        else
                            Debug.WriteLine(returnCode, "SetDNSServerSearchOrder");
                        

                        if (staticIp && !string.IsNullOrEmpty(IPv4) && !string.IsNullOrEmpty(DefaultGateWay))
                        {
                            par = o.GetMethodParameters("EnableStatic");
                            par["IPAddress"] = new string[] { IPv4 };
                            par["SubnetMask"] = new string[] { SubNetMask };
                            ret = o.InvokeMethod("EnableStatic", par, null);
                            returnCode = int.Parse(ret["returnValue"].ToString());
                            Debug.WriteLine(returnCode, "EnableStatic");


                            par = o.GetMethodParameters("SetGateways");
                            par["DefaultIPGateway"] = new string[] { DefaultGateWay };
                            ret = o.InvokeMethod("SetGateways", par, null);
                            returnCode = int.Parse(ret["returnValue"].ToString());
                            Debug.WriteLine(returnCode, "SetGateways");
                        }

                        o.InvokeMethod("EnableDHCP", null);
                        return true;
                    }
                );
            }
        }

        public int SetNewHostName()
        {
            if (string.IsNullOrEmpty(HostName) || string.Compare(Environment.MachineName, HostName, true) == 0)
                return 0;

            int returnvalue = 0;
            using (Wmi wmi = new Wmi("Win32_ComputerSystem"))
            {
                wmi.Foreach(o =>
                    {
                        ManagementBaseObject par = o.GetMethodParameters("rename");
                        par["name"] = HostName;
                        ManagementBaseObject ret = o.InvokeMethod("rename", par, null);
                        returnvalue = int.Parse(ret["returnValue"].ToString());

                        return true;
                    });
            }

            if (returnvalue == 1326)
                Debug.WriteLine("bad username or password.", "SetNewHostName");
            else
                Debug.WriteLine(returnvalue, "SetNewHostName");

            return returnvalue;
        }


        public void SetNewDomain()
        {
            if (string.IsNullOrEmpty(HostName) || string.IsNullOrEmpty(DomainUser) ||
                string.IsNullOrEmpty(DomainPassword) ||
                string.IsNullOrEmpty(DomainName))
                return;

            using (Wmi wmi = new Wmi("Win32_ComputerSystem"))
            {
                wmi.Foreach(o =>
                    {
                        ManagementBaseObject par = o.GetMethodParameters("JoinDomainOrWorkgroup");
                        par["Name"] = DomainName;
                        par["Password"] = DomainPassword;
                        par["UserName"] = DomainUser;
                        par["FJoinOptions"] = 35; // 32 + 1 + 2 
                        ManagementBaseObject ret = o.InvokeMethod("JoinDomainOrWorkgroup", par, null);

                        string result = ret["returnValue"].ToString();

                        if (setNewDomainErrors.ContainsKey(result))
                        {
                            Debug.WriteLine(setNewDomainErrors[result], "JoinDomainOrWorkgroup");
                            if (OnDomainChanged != null)
                                OnDomainChanged("JoinDomainOrWorkgroup:" + setNewDomainErrors[result]);
                        }
                        else
                            Debug.WriteLine(result, "JoinDomainOrWorkgroup");

                        return true;
                    });
            }
        }


        public void GetMessage()
        {
            DomainUser = Environment.UserName;
            using (Wmi wmi = new Wmi("Win32_ComputerSystem"))
            {
                wmi.Foreach(o =>
                {
                    DomainName = o["Domain"].ToString();
                    return true;

                });
            }


            using (Wmi wmi = new Wmi("Win32_NetworkAdapterConfiguration"))
            {
                wmi.Foreach(o =>
                {
                    if (!(bool)o["IPEnabled"])
                        return false;

                    object machineName = o["DNSHostName"]; //local machine name
                    string[] subnetMasks = o["IPSubnet"] as string[]; //arr subnet mask

                    //string[] suffixes = mo["DNSDomainSuffixSearchOrder"] as string[];
                    string[] ips = o["IPAddress"] as string[];  //arr
                    string[] gateways = o["DefaultIPGateway"] as string[];

                    for (int i = 0; i < ips.Length; i++)
                    {
                        var ip = IPAddress.Parse(ips[i]);
                        if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            HostName = machineName.ToString();
                            IPv4 = ip.ToString();
                            SubNetMask = subnetMasks[i];
                            DefaultGateWay = gateways[i];
                            break;
                        }
                    }
                    return true;

                });
            }

            NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
            List<string> dnsList = new List<string>();
            foreach (NetworkInterface adapter in adapters)
            {
                IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
                IPAddressCollection dnsServers = adapterProperties.DnsAddresses;
                if (dnsServers.Count > 0)
                {
                    foreach (IPAddress dns in dnsServers)
                    {
                        if (dns.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !dnsList.Contains(dns.ToString()))
                            dnsList.Add(dns.ToString());
                    }
                }
            }


            try
            {
                PrimaryDns = dnsList[0];
                SecondDns = dnsList[1];
            }
            catch (ArgumentOutOfRangeException)
            {

            }

        }


        internal class Wmi : IDisposable
        {
            ManagementClass mc = null;
            public Wmi(string @classname)
            {
                mc = new ManagementClass(classname);
            }

            public void Foreach(Predicate<ManagementObject> func)
            {
                var obj = mc.GetInstances();

                foreach (ManagementObject o in obj)
                {
                    if (func(o))
                        break;
                }

            }

            void IDisposable.Dispose()
            {
                if (mc != null)
                    mc.Dispose();
            }
        }


    }
}
